#include <arch/descript.h>
#include <lib/type.h>
#include<os/config.h>
#include<arch/memory.h>
#include<os/spinlock.h>

#pragma GCC optimize("O0")

DEFINE_SPIN_LOCK(descript_lock);

//make a descript
//high: b31-b24 base    b23-b20 attr  b20-b16 limit   b15-b12 attr   b11-b8 type  b7-b0 base
//low : b31-b16 base    b15-b0 limit 
des_t MakeDescript(uint32_t base, uint32_t limit, uint32_t attr, uint32_t type)
{
    des_t des;

    des.base_low = base & 0xffff;
    des.base_mid = (base >> 16) & 0xff;
    des.limit_high = ((limit >> 16) & 0xf) | ((attr) & 0xf0);
    des.access_right = ((attr & 0xf) << 4) | (type & 0xf);
    des.limit_low = limit & 0xffff;
    des.base_high = (base >> 24) & 0xff;

    return des;
}

//add a new descript in descript table last
uint16_t AddDescript(uint64_t descript, uint8_t tal_sel)
{
    uint32_t base;
    uint16_t limit;
    uint64_t *pdes;
    uint16_t des_sel;

    SpinLock(&descript_lock);

    //install to ldt
    if (tal_sel)
    {
        //get current ldt info
        base = GetLDTbase();
        limit = GetLDTlimit();
    }
    else //install to gdt
    {
        //get current Gdt info
        base = GetGDTbase();
        limit = GetGDTlimit();
    }
    //point to descript install position
    pdes = (uint64_t *)(base + limit);
    //install descript
    *pdes = descript;
    //update limit
    limit += DES_SIZE;
    //update hareware
    if (tal_sel)
    {
        LoadLDTR(base, limit);
    }
    else
    {
        LoadGDTR(base, limit);
    }
    //make descript sel
    des_sel = (limit / DES_SIZE) << 3 | DES_DPL_MASK(descript) << 1 | tal_sel;
    
    SpinUnlock(&descript_lock);
    //return des_sel
    return des_sel;
}

//update a has-present descript
int UpdateDescript(uint64_t descript, uint8_t index, uint8_t tal_sel)
{
    uint32_t base;
    uint16_t limit;
    uint64_t *pdes;

    SpinLock(&descript_lock);
    //ldt
    if (tal_sel)
    {
        //get base and limit
        base = GetLDTbase();
        limit = GetLDTlimit();
    }
    else //gdt
    {
        base = GetGDTbase();
        limit = GetGDTlimit();
    }
    //point descript postion
    pdes = (uint64_t*)(base + (index * DES_SIZE));
    //if above limit
    if ((index * DES_SIZE) < limit)
    {
        //install descript
        *pdes = descript;
        SpinUnlock(&descript_lock);
        return 0;
    }
    SpinUnlock(&descript_lock);
    return -1;
}

//install descript to index,if above table limit,update hardware
void InstallDescript(des_t descript, uint8_t index, uint8_t tal_sel)
{
    uint32_t base;
    uint16_t limit;
    volatile des_t *pdes;

    SpinLock(&descript_lock);

    //ldt
    if (tal_sel)
    {
        //get base and limit
        base = GetLDTbase();
        limit = GetLDTlimit();
    }
    else //gdt
    {
        base = GetGDTbase();
        limit = GetGDTlimit();
    }
    //point descript postion
    pdes =(des_t*)(base + (index * DES_SIZE));

    //install descript
    *pdes = descript;

    //if descript position above limit
    if (base + (index * DES_SIZE) > limit)
    {
        //limit update
        limit += DES_SIZE;
        //update hardward
        if (tal_sel)
        {
            LoadLDTR(base, limit);
        }
        else
        {
            LoadGDTR(base, limit);
        }
    }

    SpinUnlock(&descript_lock);
}

//install descript to index,if above table limit,update hardware
void InstallDescriptTo(des_t descript, uint8_t index, uint32_t base)
{
    volatile des_t *pdes;

    SpinLock(&descript_lock);

    //point descript postion
    pdes =(des_t*)(base + (index * DES_SIZE));
    //install descript
    *pdes = descript;

    SpinUnlock(&descript_lock);
}
